﻿using System;
using System.Collections.Generic;
using System.IO;

namespace Ndns.Packet
{
    public class DnsQuery: IEquatable<DnsQuery>
    {
        /// <summary>
        /// 表示DNS查询的名称。
        /// </summary>
        public DnsHostName HostName { get; set; }

        /// <summary>
        /// 表示DNS的查询类型。
        /// </summary>
        public DnsRecordType Type { get; protected set; }

        /// <summary>
        /// 表示DNS的类型。
        /// </summary>
        public DnsClass Class { get; set; }

        #region 构造DnsQuery实例

        public DnsQuery(string sHostName)
        {
            HostName = new DnsHostName(sHostName);
            Type = DnsRecordType.ANY;
            Class = DnsClass.Internet;
        }
        public DnsQuery(DnsHostName hostName)
        {
            HostName = hostName;
            Type = DnsRecordType.ANY;
            Class = DnsClass.Internet;
        }

        /// <summary>
        /// 根据相关信息构造DnsQuery实例。
        /// </summary>
        /// <param name="hostName">DNS查询的主机名信息。</param>
        /// <param name="type">DNS查询的类型。</param>
        public DnsQuery(DnsHostName hostName, DnsRecordType type)
        {
            HostName = hostName;
            Type = type;
            Class = DnsClass.Internet;
        }
        /// <summary>
        /// 根据相关信息构造DnsQuery实例。
        /// </summary>
        /// <param name="sHostName">DNS查询的主机名信息。</param>
        /// <param name="type">DNS查询的类型。</param>
        public DnsQuery(string sHostName, DnsRecordType type)
        {
            HostName = new DnsHostName(sHostName);
            Type = type;
            Class = DnsClass.Internet;
        }

        /// <summary>
        /// 根据相关信息构造DnsQuery实例。
        /// </summary>
        /// <param name="sHostName">DNS查询的主机名信息。</param>
        /// <param name="type">DNS查询的类型。</param>
        /// <param name="c">DNS查询的类别。</param>
        public DnsQuery(string sHostName, DnsRecordType type, DnsClass c)
        {
            HostName = new DnsHostName(sHostName);
            Type = type;
            Class = c;
        }

        /// <summary>
        /// 根据相关信息构造DnsQuery实例。
        /// </summary>
        /// <param name="hostName">DNS查询的主机名信息。</param>
        /// <param name="type">DNS查询的类型。</param>
        /// <param name="c">DNS查询的类别。</param>
        public DnsQuery(DnsHostName hostName, DnsRecordType type, DnsClass c)
        {
            HostName = hostName;
            Type = type;
            Class = c;
        }

        #endregion
        
        #region 构造构造DnsQuery实例实例（支持指针）

        /// <summary>
        /// 从包含数据包的流中以支持指针的方式构造DnsQuery实例。
        /// </summary>
        /// <param name="packetStream">数据包流，Position应该指向要解析的查询的开始。</param>
        /// <param name="offset">数据包的开始位置在流中的偏移量。</param>
        /// <returns>解析得到的DnsQuery实例。</returns>
        public static DnsQuery FromPacket(Stream packetStream, long packetStartIndex)
        {
            var hostName = DnsHostName.FromPacket(packetStream, packetStartIndex);
            var vType = (DnsRecordType)packetStream.ReadNetworkUInt16();
            var vClass = (DnsClass)packetStream.ReadNetworkUInt16();
            return new DnsQuery(hostName, vType, vClass);
        }
        #endregion

        #region 获取相关信息

        /// <summary>
        /// 获取字节数组表示的DnsQuery实例。
        /// </summary>
        public byte[] GetBytes()
        {
            using (var ms = new MemoryStream(512))
            {
                this.CopyTo(ms);
                return ms.ToArray();
            }
        }

        /// <summary>
        /// 返回此实例表示的内容写入到流中。
        /// </summary>
        public int CopyTo(Stream s)
        {
            int count;
            count = HostName.CopyTo(s);
            s.WriteNetworkUInt16((ushort)Type);
            s.WriteNetworkUInt16((ushort)Class);
            return count + 4;
        }

        public int CopyCompressedTo(Stream s, IDictionary<string, DnsHostNamePointer> symbols, ref int offset)
        {
            int count;
            count = HostName.CopyCompressedTo(s, symbols, ref offset);
            s.WriteNetworkUInt16((ushort)Type);
            s.WriteNetworkUInt16((ushort)Class);
            offset += 2 * sizeof(ushort);
            return count + 4;
        }

        #endregion

        #region Clone,Equals,HashCode实现
        internal DnsQuery Clone()
        {
            return new DnsQuery(HostName, Type, Class);
        }

        public override bool Equals(Object obj)
        {
            if (this == null)                        //this is necessary to guard against reverse-pinvokes and
                throw new NullReferenceException();  //other callers who do not use the callvirt instruction

            if (!(obj is DnsQuery))
                return false;

            return EqualsHelper(this, (DnsQuery)obj);
        }

        public bool Equals(DnsQuery other)
        {
            if (this == null)                        //this is necessary to guard against reverse-pinvokes and
                throw new NullReferenceException();  //other callers who do not use the callvirt instruction

            return EqualsHelper(this, other);
        }

        public static bool operator ==(DnsQuery a, DnsQuery b)
        {
            return Equals(a, b);
        }

        public static bool operator !=(DnsQuery a, DnsQuery b)
        {
            return !Equals(a, b);
        }

        public static bool Equals(DnsQuery a, DnsQuery b)
        {
            if (object.ReferenceEquals(a, b))
            {
                return true;
            }

            return EqualsHelper(a, b);
        }

        protected static bool EqualsHelper(DnsQuery a, DnsQuery b)
        {
            if ((object)a == null && (object)b == null)
                return true;

            if ((object)a == null || (object)b == null)
                return false;

            if (a.Type != b.Type)
                return false;

            if (a.Class != b.Class)
                return false;

            if (a.HostName != b.HostName)
                return false;

            return true;
        }
        public override int GetHashCode()
        {
            int total = 17;
            total = total * 37 + HostName.GetHashCode();
            total = total * 37 + Type.GetHashCode();
            total = total * 37 + Class.GetHashCode();
            return total;
        }
        #endregion

    }
}
